using System;
using System.Diagnostics;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.SharePoint;

namespace DE.Sdc.SharePoint.Common.Deployment
{
    [Serializable]
    [XmlType(AnonymousType = true, Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure")]
    [XmlRoot(Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure", IsNullable = false)]
    public class List : Folder
    {
        /// <remarks/>
        [XmlAttribute]
        public string PrimaryKeyFields { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Description { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string FeatureId { get; set; }
        
        /// <remarks/>
        [XmlAttribute]
        public int TemplateId { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public int DocTemplateId { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool NoCrawl { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool EnableModeration { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool EnableAttachments { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool EnableFolderCreation { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool UndoFileCheckout { get; set; }

        /// <remarks/>
        [XmlAnyElement]
        public XmlElement Fields { get; set; }

        /// <remarks/>
        [XmlArrayItem("ContentType")]
        public ContentType[] ContentTypes { get; set; }

        /// <summary>
        /// Adds, Replaces, Deletes, Modifies or Moves a list in a given web
        /// </summary>
        /// <param name="listDefinition">Xml object for a list</param>
        /// <param name="list">A list based on the listDefinition or null</param>
        /// <param name="parentWeb">The parent where to import the list</param>
        protected static internal void ImportList(List listDefinition, SPList list, SPWeb parentWeb)
        {
            Trace.WriteLine("Try to import list '" + listDefinition.Title + "' with url '" + listDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ImportList");

            if (list == null)
            {
                if (listDefinition.Action == ActionType.Add || listDefinition.Action == ActionType.Replace)
                    list = CreateList(listDefinition, parentWeb);
                else
                    return;
            }
            else if (listDefinition.Action == ActionType.Replace)
            {
                DeleteList(list);
                list = CreateList(listDefinition, parentWeb);
            }
            else if (listDefinition.Action == ActionType.Delete)
            {
                DeleteList(list);
                return;
            }

            if (listDefinition.Action != ActionType.None &&
                listDefinition.Action <= ActionType.Modify)
                ModifyList(listDefinition, list);

            new Folder().ImportFolder(listDefinition, list.RootFolder, null, listDefinition.PrimaryKeyFields != null ? listDefinition.PrimaryKeyFields.Split(new[] { ',', ';' }) : null);
            Permissions.SetPermissions(listDefinition, list, list.ParentWeb);
            list.Update();

            if (list.ParentWeb != parentWeb)
                list.ParentWeb.Dispose();

            Trace.WriteLine("List '" + listDefinition.Title + "' with url '" + listDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ImportList");
        }

        /// <summary>
        /// Creates a list in a given web
        /// </summary>
        /// <param name="listDefinition">Xml object for a list</param>
        /// <param name="parentWeb">The parent web where to create the list</param>
        /// <returns>The created list</returns>
        private static SPList CreateList(List listDefinition, SPWeb parentWeb)
        {
            string listName = ResolveResource(listDefinition.Title, parentWeb.Locale);
            string listDescription = ResolveResource(listDefinition.Description, parentWeb.Locale);
            string listUrl = ResolveResource(listDefinition.Url, parentWeb.Locale);
            Trace.WriteLine("Try to create list '" + listDefinition.Title + "' with url '" + listDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:CreateList");

            Guid listId = parentWeb.Lists.Add(listName, listDescription,
                                              listUrl ?? listName, listDefinition.FeatureId,
                                              listDefinition.TemplateId == 0 ? 100 : listDefinition.TemplateId,
                                              listDefinition.DocTemplateId == 0
                                                  ? "100"
                                                  : listDefinition.DocTemplateId.ToString());

            Trace.WriteLine("List '" + listDefinition.Title + "' with url '" + listDefinition.AbsoluteUrl + "' created.", "ContentStructureImporter:CreateList");
            return parentWeb.Lists[listId];
        }

        /// <summary>
        /// Modifies the properties for a list 
        /// </summary>
        /// <param name="listDefinition">Xml object for a list</param>
        /// <param name="list">The list which properties should updated</param>
        private static void ModifyList(List listDefinition, SPList list)
        {
            Trace.WriteLine("Try to modify list '" + listDefinition.Title + "' with url '" + listDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ModifyList");
            using (SPWeb parentWeb = list.ParentWeb)
            {
                list.NoCrawl = listDefinition.NoCrawl;
                list.EnableModeration = listDefinition.EnableModeration;
                list.EnableAttachments = listDefinition.EnableAttachments;
                list.EnableFolderCreation = listDefinition.EnableFolderCreation;

                if (listDefinition.Fields != null)
                {
                    XmlNodeList fieldNodes = listDefinition.Fields.ChildNodes;

                    for (int index = 0; index < fieldNodes.Count; index++)
                    {
                        try
                        {
                            XmlNode fieldNode = fieldNodes[index];
                            string fieldName = fieldNode.Attributes["Name"].Value;
                            string fieldDisplayName = fieldNode.Attributes["DisplayName"].Value;

                            if (!list.Fields.ContainsField(fieldName))
                            {
                                if (fieldNode.Attributes["Type"].Value == "Lookup")
                                {
                                    XmlNode listNode = fieldNode.Attributes.GetNamedItem("List");
                                    if (listNode != null)
                                        listNode.Value = parentWeb.Lists[listNode.Value].ID.ToString();
                                }
                                fieldNode.Attributes["DisplayName"].Value = fieldName;
                                fieldName = list.Fields.AddFieldAsXml(RemoveNamespace(fieldNode.OuterXml, fieldNode.NamespaceURI), true,
                                                                      SPAddFieldOptions.Default);
                                fieldNode.Attributes["DisplayName"].Value = fieldDisplayName;
                            }

                            SPField field = list.Fields.GetField(fieldName);
                            field.SchemaXml = fieldNode.OuterXml;
                            //XmlNamespaceManager namespaceManager = new XmlNamespaceManager(fieldNode.OwnerDocument.NameTable);
                            //namespaceManager.AddNamespace("x", fieldNode.NamespaceURI);
                            //XmlNodeList propertyNodes = fieldNode.SelectNodes(".//x:Customization/x:ArrayOfProperty/x:Property", namespaceManager);
                            //foreach (XmlNode propertyNode in propertyNodes)
                            //{
                            //    string name = propertyNode["Name"].InnerText;
                            //    string value = propertyNode["Value"] != null ? propertyNode["Value"].InnerText : "";

                            //    field.SetCustomProperty(name,value);
                            //}
                            field.Update();
                        }
                        catch (SPException)
                        {
                        }
                    }
                }

                ContentType[] contentTypes = listDefinition.ContentTypes;
                if (contentTypes != null && contentTypes.Length > 0)
                {
                    list.ContentTypesEnabled = true;

                    for (int index = 0; index < contentTypes.Length; index++)
                    {
                        ContentType contentTypeDefinition = contentTypes[index];
                        string contentTypeId = ResolveResource(contentTypeDefinition.Id, parentWeb.Locale);
                        SPContentType contentType = contentTypeDefinition.IdQualifier == QualifierType.Id
                                                        ? parentWeb.AvailableContentTypes[
                                                              new SPContentTypeId(contentTypeId)]
                                                        : parentWeb.AvailableContentTypes[contentTypeId];


                        if (contentType != null && 
                            (contentTypeDefinition.Action == ActionType.Delete || contentTypeDefinition.Action == ActionType.Replace))
                            list.ContentTypes.Delete(contentType.Id);

                        if (contentTypeDefinition.Action == ActionType.Add || 
                            contentTypeDefinition.Action == ActionType.Replace)
                        {
                            if (contentType == null)
                                throw new ArgumentOutOfRangeException(
                                    String.Format("ContentType with {0} '{1}' could not be found",
                                                  contentTypeDefinition.IdQualifier, contentTypeId));

                            if (list.ContentTypes[contentType.Name] == null && list.ContentTypes[contentType.Id] == null)
                            {
                                try
                                {
                                    list.ContentTypes.Add(contentType);
                                }
                                catch (Exception)
                                {
                                    //TODO: Loggen
                                }
                            }
                        }

                        if (contentType != null && contentTypeDefinition.Action != ActionType.None &&
                            contentTypeDefinition.Action <= ActionType.Modify)
                        {
                            for (int fieldIndex = 0; fieldIndex < contentType.Fields.Count; fieldIndex++)
                            {
                                SPField contentTypeField = contentType.Fields[fieldIndex];
                                SPField listField = null;

                                if (list.Fields.ContainsField(contentTypeField.InternalName))
                                {
                                    listField = list.Fields.GetField(contentTypeField.InternalName);
                                    if (contentTypeField.Type != listField.Type)
                                    {
                                        listField.Type = contentTypeField.Type;
                                        listField.Update(true);
                                    }
                                }
                                else
                                {
                                    SPFieldCollection fieldCollection = list.Fields;
                                    string fieldname = fieldCollection.Add(contentTypeField.InternalName, contentTypeField.Type, contentTypeField.Required);
                                    SPField field = fieldCollection.GetField(fieldname);
                                    field.Title = contentTypeField.Title;
                                    field.Update();
                                    //fieldCollection.AddFieldAsXml(contentTypeField.SchemaXml, true, SPAddFieldOptions.Default);
                                    list.Update();
                                }
                            }
                        }
                    }
                }

                ModifyObjectProperties(listDefinition, list);

                list.Update();
            }
            Trace.WriteLine("List '" + listDefinition.Title + "' with url '" + listDefinition.AbsoluteUrl + "'. modified", "ContentStructureImporter:ModifyList");       
        }

        /// <summary>
        /// Deletes a list
        /// </summary>
        /// <param name="list">The list to delete</param>
        private static void DeleteList(SPList list)
        {
            Trace.WriteLine("Try to delete list '" + list.Title + "' with url '" + list.ParentWebUrl + "'.", "ContentStructureImporter:DeleteList");
            list.Delete();
            Trace.WriteLine("List '" + list.Title + "' with url '" + list.ParentWebUrl + "' deleted.", "ContentStructureImporter:DeleteList");
            
        }
    }
}